# -*- coding: utf-8 -*-
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".

Replace these with more appropriate tests for your application.
"""

from django.test import TestCase
from django.db.models import Count
import models
from converter import Converter, alias, convert

class ConversionTest(TestCase):
    def setUp(self):
        self.street1 = 'Broadway'
        self.house1 = 10
        self.apartment1 = 5
        self.street2 = 'Wall Street'
        self.house2 = 2
        self.apartment2 = 3

        self.address1 = models.Address.objects.create(street = self.street1, house = self.house1, apartment = self.apartment1)
        self.address2 = models.Address.objects.create(street = self.street2, house = self.house2, apartment = self.apartment2)

        self.name1 = 'John'
        self.name2 = 'James'
        self.name3 = 'Smith'
        self.name4 = 'Jack'
        self.name5 = 'Alex'

        self.person1 = models.Person.objects.create(name = self.name1, age = 20, address = self.address1)
        self.person2 = models.Person.objects.create(name = self.name2, age = 20, address = self.address2)
        self.person3 = models.Student.objects.create(name = self.name3, age = 17, address = self.address1, grade = 3)
        self.person4 = models.SubclassedStudent.objects.create(name = self.name4, age = 21, address = self.address2, grade = 1)
        self.person5 = models.Worker.objects.create(name = self.name5, age = 30, address = self.address2, experience = 7)

        self.hobby1 = models.Hobby.objects.create(name = 'coding')
        self.hobby2 = models.Hobby.objects.create(name = 'reading')

        self.person1.hobby.add(self.hobby1)
        self.person1.hobby.add(self.hobby2)
        self.person1.save()
        self.person2.hobby.add(self.hobby1)
        self.person2.save()

    def testSimpleConversion(self):
        conv = Converter({ 'Address' : ['street', 'house'] })
        self.assertEqual(conv.convert(self.address1), { 'street' : self.street1, 'house' : self.house1 })

    def testConvertShortcut(self):
        context = { 'Address' : ['street', 'house'] }
        self.assertEqual(convert(self.address1, context), { 'street' : self.street1, 'house' : self.house1 })

    def testQuerySetConversion(self):
        conv = Converter({ 'Person' : [ 'name', 'address'], 'Address' : [ 'house', 'apartment'] })
        self.assertEqual(conv.convert(self.person1), { 'name' : self.name1, 'address' : { 'house' : self.house1, 'apartment' : self.apartment1 } })

    def testAlias(self):
        conv = Converter({ 'Address' : [ alias('where', 'street') ] })
        self.assertEqual(conv.convert(self.address1), { 'where' : self.street1 } )

    def testSubclass(self):
        conv = Converter({ 'Student' : ['grade', 'age' ] })
        self.assertEqual(conv.convert(self.person3.person_ptr), { 'grade' : 3, 'age' : 17 })
        self.assertEqual(conv.convert(self.person3), { 'grade' : 3, 'age' : 17 })

    def testQuerySet(self):
        conv = Converter({ 'Person' : [ 'name', 'age' ]})
        SEARCH_AGE = 20
        query = models.Person.objects.filter(age = SEARCH_AGE)
        self.assertEqual(conv.convert(query), [ { 'name' : self.name1, 'age' : SEARCH_AGE }, { 'name' : self.name2, 'age' : SEARCH_AGE } ])

    def testMultipleResolution(self):
        conv = Converter({ 'Student' : ['grade'], 'SubclassedStudent' : ['age'] })
        self.assertRaises(Converter.MultipleResolutionError, conv.convert, self.person4.student_ptr.person_ptr)

    def testFormatError(self):
        self.assertRaises(Converter.ContextFormatError, lambda: Converter({ 'Student' : 'age' }))

    def testMultipleAncestors(self):
        conv = Converter({ 'Student' : ['grade'], 'Worker' : ['experience'] })
        data = [ self.person3.person_ptr, self.person5.person_ptr ]
        self.assertEqual(conv.convert(data), [ { 'grade' : self.person3.grade }, { 'experience' : self.person5.experience } ])

    def testFunction(self):
        conv = Converter({ 'Person' : [('initial', lambda x: x.name[0])] })
        self.assertEqual(conv.convert(self.person1), { 'initial' : self.name1[0] })

    def testManyToMany(self):
        conv = Converter({ 'Person' : ['hobby'], 'Hobby' : ['name'] })
        data = models.Person.objects.annotate(hobby_count = Count('hobby')).filter(hobby_count__gt = 0)
        self.assertEqual(conv.convert(data), [ { 'hobby' : [{ 'name' : 'coding'}, { 'name' : 'reading'}]}, { 'hobby' : [{ 'name' :'coding' }] }])

    def testManyToManyFunction(self):
        """
        To avoid too many data structures, we can use a function.
        """
        conv = Converter({ 'Person' : [('hobbies', lambda x: map(lambda x: x.name, x.hobby.all()))] })
        data = models.Person.objects.annotate(hobby_count = Count('hobby')).filter(hobby_count__gt = 0)
        self.assertEqual(conv.convert(data), [ { 'hobbies' : ['coding', 'reading'] }, { 'hobbies' : ['coding'] } ])

    def testDict(self):
        """
        Converting a dictionary.
        """
        conv = Converter({ 'Address' : ['street'] })
        data = { 'address1' : self.address1, 'address2' : self.address2 }
        self.assertEqual(conv.convert(data), { 'address1' : { 'street' : self.street1 }, 'address2' : { 'street' : self.street2 } })
